package com.space.laboratory.redis;

import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RCountDownLatch;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author <a href="mailto:414563035@qq.com">Bedroom</a>
 * @date 2021/6/25 11:15 上午
 */
@SpringBootApplication
@Slf4j
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Configuration
    public static class A implements CommandLineRunner {
        @Bean
        public RedisTemplate<String, Object> redisTemplate(RedissonConnectionFactory redissonConnectionFactory){
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(new GenericFastJsonRedisSerializer());
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(new GenericFastJsonRedisSerializer());
            redisTemplate.setConnectionFactory(redissonConnectionFactory);
            return redisTemplate;
        }

        @Value("${spring.redis.host}")
        private String redisHost;
        @Value("${spring.redis.port}")
        private String redisPort;

        @Bean
        public RedissonClient redissonClient() {
            Config config = new Config();
//		config.useSingleServer().setAddress("redis://127.0.0.1:6379");
            config.useSingleServer().setAddress("redis://" + redisHost + ":" + redisPort);
            return Redisson.create(config);
        }

        @Autowired
        private RedisTemplate redisTemplate;

        CountDownLatch jdkCountDownLatch = new CountDownLatch(1);

        @Override
        public void run(String... args) throws Exception {
            final ExecutorService executorService = Executors.newFixedThreadPool(30);
            for (int i = 0; i < 30; i++) {
                executorService.execute(this::productCache);
            }

            jdkCountDownLatch.countDown();
        }

        private void productCache() {
            try {
                jdkCountDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            final RCountDownLatch countDownLatch = redissonClient().getCountDownLatch("CountDownLatch");
            countDownLatch.trySetCount(1L);
            if (redisTemplate.opsForValue().setIfAbsent("lock", "lock", Duration.ofSeconds(3))) {
                try {
                    log.info("模拟查找数据库3S");
                    Thread.sleep(1000L);
                    log.info("查找完毕.设置数据");
                    redisTemplate.opsForValue().set("product", "productData");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    log.info("最后退出, 删除锁， 放行别的");
                    redisTemplate.delete("lock");
                    countDownLatch.countDown();
                }
            } else {
                try {
                    log.info("等待数据库数据进行设置缓存");
                    countDownLatch.await(3L , TimeUnit.SECONDS);
                    log.info("数据库缓存设置完毕， 开始从redis获取缓存");
                    log.info("取得设置的数据:" + redisTemplate.opsForValue().get("product"));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
